package com.yc.boot.sys.business;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.yc.boot.common.model.response.Outcome;
import com.yc.boot.common.utils.RedisUtil;
import com.yc.boot.sys.entity.SysUser;
import com.yc.boot.sys.config.SsoConfigEnum;
import com.yc.boot.sys.model.Login;
import com.yc.boot.sys.model.Token;
import com.yc.boot.sys.model.UserInfo;
import com.yc.boot.sys.utils.PasswordUtil;
import com.yc.boot.sys.utils.TokenUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.util.Set;

import static com.yc.boot.common.constant.BaseConstant.*;
import static com.yc.boot.common.constant.RedisConstant.SYS_USER_LOGIN;


/**
 * @author 杨智杰
 * @since 2021/8/12 16:33
 */
@Service
public class SysBusiness {

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private SysUserBusiness sysUserBusiness;

    @Resource
    private SysUtilBusiness sysUtilBusiness;

    /**
     * token有效期最短3600秒(1小时)
     */
    @Value("${login.token.min-valid-time:3600}")
    private Long minValidTime;

    /**
     * token有效期最长2592000秒(30天)
     */
    @Value("${login.token.max-valid-time:2592000}")
    private Long maxValidTime;

    /**
     * token有效期默认28800秒(8小时)
     */
    @Value("${login.token.default-valid-time:28800}")
    private Long defaultValidTime;

    /**
     * 是否开启单点登录模式
     */
    @Value("${login.sso.enable:false}")
    private Boolean ssoEnable;

    /**
     * 单点登录策略配置
     */
    @Value("${login.sso.strategy:compel}")
    private SsoConfigEnum ssoStrategy;

    /**
     * 是否开启图片验证码校验
     */
    @Value("${login.enable-auth-code:false}")
    private Boolean enableAuthCode;


    /**
     * 登录
     *
     * @param param 登录信息
     * @return 登录成功响应token信息
     */
    public Outcome<Token> login(Login param) {
        if (enableAuthCode) {
            if (StrUtil.isBlank(param.getSerial()) || StrUtil.isBlank(param.getCode())) {
                return Outcome.failure("未填写验证码!");
            }
            if (!sysUtilBusiness.verifyCode(param.getSerial(), param.getCode())) {
                return Outcome.failure("验证码错误!");
            }
        }
// 根据账号从数据库或者redis缓存获取登录用户的信息（此时的信息已经经过了加密） 与后面的通过MD5加盐加密算法计算出的值作比较
        SysUser user = sysUserBusiness.getUserByLoginName(param.getLoginName());
        if (ObjectUtil.isNotNull(user) && user.getPassword()
                .equals(PasswordUtil.getPassWord(user.getSale(), param.getPassword()))) {
            if (user.getStatus() == ZERO) {
                return Outcome.failure("用户已被停用!");
            }
// 是否开启sso单点登录
            if (ssoEnable) {
//                                               如果键值 sys:user:login:*.加密后的登录账号.* 不为空就获取缓存中的所有的与user账号相等的key然后删除
                Set<String> key = redisUtil.dimGetKey(SYS_USER_LOGIN + "*." + SecureUtil.md5(user.getLoginName()) + ".*");
                if (CollUtil.isNotEmpty(key)) {
                    if (SsoConfigEnum.compel.equals(ssoStrategy)) {
                        key.stream().forEach(k -> redisUtil.del(k));
                    }
                    if (SsoConfigEnum.refuse.equals(ssoStrategy)) {
                        return Outcome.failure("该账户已在其他地方登录!");
                    }
                }
            }
//            登录最后返回的就是token令牌包含请求头 ，令牌，当前登录有效时长，后面根据令牌来访问接口
            Token token = new Token().setHeader(AUTHORIZATION).setToken(TokenUtil.makeToken(user.getLoginName()));
            if (ObjectUtil.isNull(param.getTokenValidTime()) || param.getTokenValidTime() <= ZERO) {
                token.setValidTime(defaultValidTime);
            } else if (param.getTokenValidTime() < minValidTime) {
                token.setValidTime(minValidTime);
            } else if (param.getTokenValidTime() > maxValidTime) {
                token.setValidTime(maxValidTime);
            } else {
                token.setValidTime(defaultValidTime);
            }
            UserInfo userInfo = sysUserBusiness.getUserInfo(user);
            redisUtil.set(SYS_USER_LOGIN + token.getToken(), userInfo, token.getValidTime());
            sysUserBusiness.update((SysUser) new SysUser().setLoginCount(user.getLoginCount() + ONE)
                            .setLoginHost(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                                    .getRequest().getRemoteHost()).setLoginTime(DateUtil.now()).setId(user.getId()));
            return Outcome.success(token);
        }
        return Outcome.failure("用户名或密码错误!");
    }

    /**
     * 登录校验
     *
     * @param token
     * @return
     */
    public Boolean loginCheck(String token) {
        if (StrUtil.isNotBlank(token) && redisUtil.hasKey(SYS_USER_LOGIN + token)) {
            Long expirationTime = redisUtil.getExpire(SYS_USER_LOGIN + token);
            if (expirationTime != ZERO && expirationTime < minValidTime) {
                redisUtil.expire(SYS_USER_LOGIN + token, expirationTime + minValidTime);
            }
            return true;
        }
        return false;
    }


    /**
     * 注销
     *
     * @param token
     */
    public void logout(String token) {
        redisUtil.del(SYS_USER_LOGIN + token);
    }
}
